﻿/// <reference name="MicrosoftAjax.js"/>

(function() {
    var scriptName = "MenuExtender";

function execute() {

    Type.registerNamespace("SCS");

    SCS.MenuType = function () {
        throw Error.invalidOperation();
    };

    SCS.MenuType.prototype = {
        Vertical: 0,
        Horizontal: 1
    };

    SCS.MenuType.registerEnum('SCS.MenuType');

    SCS.MenuBehavior = function (element) {
        SCS.MenuBehavior.initializeBase(this, [element]);

        this._items = [];
        this._hideDelay = 1.5;
        this._onKeyDelegate = null;
        this._menuClass = null;
        this._itemClass = null;
        
        this.cssClasses = {};
    
        this.menuType = null;
    }

    SCS.MenuBehavior.prototype = {
        initialize: function () {
            SCS.MenuBehavior.callBaseMethod(this, 'initialize');

            //Sys.Debug.fail("");

            this.cssClasses.item = this._itemClass;
            this.cssClasses.itemHover = this._itemClass + "-hover";
            this.cssClasses.topItem = this._itemClass + "-top";
            this.cssClasses.topItemHover = this._itemClass + "-top-hover";
            this.cssClasses.itemDisabled = this._itemClass + "-disabled";

            var elem = $(this.get_element()),
                rootItems = elem.children();

            elem.wrap(String.format("<span class='{0}'></span>", this._menuClass));

            var baseId = this.get_id(),
                level = -1;

            for (var i = 0; i < rootItems.length; i++)
                this._buildItems(rootItems[i], this._items, null, baseId + "_" + i, level);

            this.menuType = (rootItems.css("float") != "left") ? SCS.MenuType.Vertical : SCS.MenuType.Horizontal;

            this._onKeyDelegate = Function.createDelegate(this, this._onKeyDown);
            $(document).bind("keydown", this._onKeyDelegate);
        },

        dispose: function () {

            for (var i = 0; i < this._items.length; i++)
                this._disposeItems(this._items[i]);

            $(document).unbind("keydown", this._onKeyDelegate);

            SCS.MenuBehavior.callBaseMethod(this, 'dispose');
        },

        find: function (text) {

            var anchor = $(String.format("li > a:contains('{0}')", text), this.get_element());

            if (anchor)
                return anchor.parent("li").attr("mi");

            return null;
        },

        _disposeItems: function (item) {

            if (item.items)
                for (var i = 0; i < item.items.length; i++)
                    this._disposeItems(item.items[i]);

            item.dispose();
        },

        _buildItems: function (elem, items, parentItem, id, level) {

            level++;

            var newItem = new SCS.MenuItem
            {
                newItem.id = id;
                newItem.element = elem;
                newItem.parent = (parentItem) ? parentItem : null;
                newItem.root = this;
                newItem.level = level;
            };

            newItem.initialize();

            elem.className = (level == 0) ? this.cssClasses.topItem : this.cssClasses.item;
            elem.mi = newItem;

            Array.add((parentItem) ? parentItem.items : items, newItem);

            var childItems = $("ul", elem).eq(0).children();

            if (childItems.length > 0)
                $(elem).children("a").append("<span/>");

            for (var i = 0; i < childItems.length; i++)
                this._buildItems(childItems[i], items, newItem, id + "_" + i, level);
        },

        _onKeyDown: function (e) {

            var item = $(e.target).parent("li").attr("mi");

            if (item) {

                e.preventDefault();

                var elem = $(item.element),
                    next = null,
                    menu = null,
                    keyCode = e.keyCode;

                //Sys.Debug.fail("");

                // special case for top level items on horizontal menu.
                if (this.menuType == SCS.MenuType.Horizontal && item.level == 0) {

                    switch (keyCode) {

                        case 37: // left key 
                            keyCode = 38; // change to up key
                            break;
                        case 39: // right key
                            keyCode = 40; // change to down key
                            break;
                        case 38: // up key 
                            keyCode = 37; // change to left key
                            break;
                        case 40: // down key
                            keyCode = 39; // change to right key
                            break;
                    }
                }

                switch (keyCode) {

                    // tab key                                                                              
                    case 9:

                        if (e.shiftKey) {

                            next = elem.prev("li");

                            if (next.length < 1) {

                                next = elem.parents("li").eq(0);

                                if (next.length > 0) {
                                    // go to owner node in parent menu and close child menu
                                    next.attr("mi").hideMenu();
                                }

                                if (next.length < 1) {
                                    // go to last item in current menu 
                                    next = elem.parent("ul").children("li").last();
                                }
                            }
                        }
                        else {

                            menu = elem.children("ul");

                            if (menu.length > 0) {

                                // go to first item in child menu
                                next = menu.children("li").first();

                                // ensure other open menus are closed first
                                this.hideMenus();

                                // show child menu
                                next.attr("mi").showMenu();
                            }
                            else {
                                next = elem.next();

                                var hideChild = null;

                                if (next.length < 1) {
                                    // go to next item in parent menu and close child menu
                                    next = elem.parents("li").eq(0).next("li");
                                    hideChild = 1;
                                }

                                if (next.length < 1) {
                                    // go to first item in parent menu and close child menu
                                    next = elem.parents("ul").eq(1).children("li").first();
                                    hideChild = 1;
                                }

                                if (hideChild)
                                    elem.parents("li").attr("mi").hideMenu();
                            }
                        }
                        break;

                    // escape key                                                                              
                    case 27:

                        // left arrow                       
                    case 37:

                        menu = elem.parents("ul").eq(1);

                        if (menu.length > 0) {
                            // hide the child menu we're leaving
                            next = elem.parents("li").eq(0);
                            next.attr("mi").hideMenu();
                        }
                        break;

                    // right arrow                                                                               
                    case 39:

                        menu = elem.children("ul");

                        if (menu.length > 0) {
                            // current node contains a menu so show it
                            item.showMenu();
                            next = menu.children("li").first();
                        }
                        break;

                    // up arrow	                                                                              
                    case 38:

                        next = elem.prev();

                        if (next.length < 1) {
                            // move to next item in current menu
                            next = elem.parent("ul").children("li").last();
                        }

                        break;

                    // down arrow                                                                              
                    case 40:

                        next = elem.next();

                        if (next.length == 0) {
                            // move to previous item in current menu
                            next = elem.parent("ul").children("li").first();
                        }
                        break;
                }

                if (next && next.length > 0) {

                    // go to the next selected item
                    var nextItem = next.attr("mi");
                    nextItem.focus();
                    nextItem.highlight(true);

                    // ensure mouse-over effect is removed from previous item
                    item.highlight(false);
                }
            }
        },

        hideMenus: function () {

            this._hideMenusRecursive(this._items);
        },

        _hideMenusRecursive: function (items) {

            for (var i = 0; i < items.length; i++) {

                items[i].hideMenu();

                var childItems = items[i].items;

                if (childItems.length > 0)
                    this._hideMenusRecursive(childItems);
            }
        },

        get_Items: function () {
            return this._items;
        },

        get_HideDelaySeconds: function () {
            return this._hideDelay;
        },

        set_HideDelaySeconds: function (value) {
            this._hideDelay = value;
        },

        get_MenuCssClass: function () {
            return this._menuClass;
        },

        set_MenuCssClass: function (value) {
            this._menuClass = value;
        },

        get_ItemCssClass: function () {
            return this._itemClass;
        },

        set_ItemCssClass: function (value) {
            this._itemClass = value;
        }
    }
    SCS.MenuBehavior.registerClass('SCS.MenuBehavior', Sys.UI.Behavior);

    if (Sys.registerComponent)
        Sys.registerComponent(SCS.MenuBehavior, { name: "menuBehavior", parameters: [{ name: "MenuCssClass", type: "String" }, { name: "ItemCssClass", type: "String"}] });

    SCS.MenuItem = function () {

        this.id = null;
        this.element = null;
        this.parent = null;
        this.root = null;
        this.items = [];
        this.level = null;

        this._a = null;
        this._ul = null;
        this._el = null;

        this._timout = null;

        this._showDelegate = null;
        this._hideDelegate = null;
        this._clickDelegate = null;
    }

    SCS.MenuItem.prototype = {
        initialize: function () {

            this._el = $(this.element);
            this._a = $(this._el).children("a");
            this._ul = $(this._el).children("ul");

            this._showDelegate = Function.createDelegate(this, this._onMouseover);
            this._el.bind("mouseover.mi", this._showDelegate);

            this._hideDelegate = Function.createDelegate(this, this._onMouseout);
            this._el.bind("mouseout.mi", this._hideDelegate);

            this._clickDelegate = Function.createDelegate(this, this._onClick);
            this._el.bind("click.mi", this._clickDelegate);
        },
        dispose: function () {

            this._el.unbind("mi");
            this._showDelegate = this._hideDelegate = this._clickDelegate = null;

            this.element.mi = this.element = null;
        },

        _onMouseover: function (e) {

            this.highlight(true);
            this.showMenu();

            e.stopPropagation();
        },

        _onMouseout: function (e) {

            this.highlight(false);

            if (this.items.length == 0)
                return;

            var delay = this.root.get_HideDelaySeconds();

            if (!delay) {
                this.hideMenu();
            }
            else {
                this._timout = window.setTimeout(Function.createDelegate(this, this.hideMenu), delay * 1000);
            }
        },

        _onClick: function (e) {

            this.root.hideMenus();
        },

        showMenu: function () {

            // close any open menus
            this.root.hideMenus(this);

            if (this.get_Enabled()) {

                // to show this menu we need to ensure
                // all parent menus are shown as well
                var menu = this;

                while (menu = menu.parent)
                    menu._showMenu();

                // show this menu
                if (this.items.length > 0)
                    this._showMenu();
            }
        },

        _showMenu: function () {
            this._cancelHide();
            this._ul.show();
        },

        _cancelHide: function () {

            if (this._timout)
                window.clearTimeout(this._timout);

            this._timout = null;
        },

        hideMenu: function () {
            this._ul.hide();
        },

        focus: function () {
            this._a.get(0).focus();
        },

        highlight: function (state) {

            if (this.get_Enabled()) {

                var className = (this.level == 0) ? this.root.cssClasses.topItemHover : this.root.cssClasses.itemHover;

                if (state)
                    this._el.addClass(className);
                else
                    this._el.removeClass(className);
            }
        },

        get_Text: function () {
            return this._a.text();
        },

        set_Text: function (value) {
            this._a.text(value);
        },

        get_Enabled: function () {
            return !this._a.attr("disabled");
        },

        set_Enabled: function (value) {
            this._a.attr("disabled", !value);
        }
    }

} // execute

if (window.Sys && Sys.loader) {
    Sys.loader.registerScript(scriptName, [], execute);
}
else {
    execute();
}

})();